/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package modelo;

import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import java.util.*;
import java.util.ArrayList;
import com.vividsolutions.jts.simplify.DouglasPeuckerLineSimplifier;

/**
 *
 * @author Diego
 */
public class NAA {

    private List<Nodo> nodos = new ArrayList<Nodo>();
    private List<ArcoDirigido> arcos = new ArrayList<ArcoDirigido>();
    private Set<Punto> conjuntoInicial;
    private Set<Punto> conjuntoGenerado;
    private Set<SegmentoLinea> segmentosCerrados;
    private List<SegmentoLinea> segmentosIniciales=new ArrayList<SegmentoLinea>();

    public void setSegmentosCerrados(Set<SegmentoLinea> pSegmentos)
    {
        segmentosCerrados = pSegmentos;
    }

    public void setConjuntoInicial(Set<Punto> pPuntos)
    {
        conjuntoInicial = pPuntos;
    }

     public void enviarProcesoInicial()
    {
        Iterator<SegmentoLinea> recorrido = segmentosCerrados.iterator();
        if(recorrido.hasNext())
        {
            SegmentoLinea nuevo = recorrido.next();
            enviarCiclosMayores(nuevo);
        }
    }

    public void enviarCiclosMayores(SegmentoLinea pSegmento)
    {
        int k = generarNAA(pSegmento);
        if(k>=0)
        {
            conjuntoGenerado = getArcos().get(k).toConjuntoPuntos();
            conjuntoInicial = Punto.removerPuntos(conjuntoInicial, conjuntoGenerado);
        }
        if(conjuntoInicial.size()>0)
        {
            Iterator<Punto> recorrido = conjuntoInicial.iterator();
            if(recorrido.hasNext())
            {
                Punto nPunto = recorrido.next();
                SegmentoLinea nSegmento = SegmentoLinea.puntoIncidente(segmentosCerrados, nPunto);
                enviarCiclosMayores(nSegmento);
            }
        }
    }

    public int generarNAA(SegmentoLinea arcoInicial)
    {
        List<SegmentoLinea> recorridoArcos = new ArrayList<SegmentoLinea>();
        recorridoArcos.add(arcoInicial);
        recorridoArcos = realizarRecorridoInicial(arcoInicial, recorridoArcos,arcoInicial.getFin(),arcoInicial.getOrigen());
        if(recorridoArcos==null)
            return -1;
        else
        {
            int k = getArcos().size();
            PoliLinea pNueva = PoliLinea.toPoliLinea(recorridoArcos);
            if(pNueva.esArea())
            {
                Nodo nNuevo = new Nodo();
                nNuevo.setPunto(arcoInicial.getOrigen());
                String nEtiqueta = Nodo.existeEnListadoNodos(nodos, nNuevo);
                if(nEtiqueta==null)
                {
                    int  et = nodos.size();
                    et++;
                    String sEt= String.valueOf(et);
                    nNuevo.setEtiqueta(sEt);
                    nodos.add(nNuevo);
                }
                else nNuevo.setEtiqueta(nEtiqueta);

                ArcoDirigido aNuevo = new ArcoDirigido();
                aNuevo.setPoliLinea(pNueva.getPoliLinea());
                aNuevo.setInicial(nNuevo);
                aNuevo.setFinal(nNuevo);
                if(!ArcoDirigido.existeEnListadoArcos(arcos, aNuevo))
                {
                    char et = Alfabeto.MINUSCULAS[getArcos().size()];
                    String sEt= String.valueOf(et);
                    aNuevo.setEtiqueta(sEt);
                    getArcos().add(aNuevo);
                }
                
                return k;
            }
            else return -1;
        }
    }

    public List<SegmentoLinea> realizarRecorridoInicial(SegmentoLinea arcoInicial,List<SegmentoLinea> anteriores,Punto puntoInicio,Punto puntoPartidaGeneral)
    {
        Set<SegmentoLinea> incidentes = SegmentoLinea.segmentosPuntoFinal(segmentosCerrados,arcoInicial,puntoInicio);
        //Si tiene varios caminos
        if(incidentes.size()>1)
        {
            segmentosIniciales.add(arcoInicial);
            Iterator<SegmentoLinea> recorrido = incidentes.iterator();
            while(recorrido.hasNext())
            {
                SegmentoLinea nuevo = recorrido.next();
                if(!SegmentoLinea.existeEnListaSegmentos(nuevo,segmentosIniciales))
                {
                    segmentosIniciales.add(nuevo);
                    List<SegmentoLinea> recorridoArcos = new ArrayList<SegmentoLinea>();
                    recorridoArcos.add(nuevo);
                    Punto siguiente = nuevo.getFin();
                    if(siguiente.esIgual(puntoInicio))
                        siguiente = nuevo.getOrigen();
                    realizarRecorrido(nuevo, recorridoArcos,siguiente);
                }
            }
            return null;
        }
        Iterator<SegmentoLinea> recorrido = incidentes.iterator();
        if(recorrido.hasNext())
        {
            SegmentoLinea siguiente = recorrido.next();
            if(!SegmentoLinea.existeEnListaSegmentos(siguiente,anteriores))
            {
                Punto envio = siguiente.getFin();
                if(envio.esIgual(puntoInicio))
                    envio = siguiente.getOrigen();
                anteriores.add(siguiente);
                if(!envio.esIgual(puntoPartidaGeneral))
                {
                    realizarRecorridoInicial(siguiente,anteriores,envio,puntoPartidaGeneral);
                }
            }
        }
        return anteriores;
    }

    public void imprimirNAA()
    {
        ArcoDirigido.imprimirListadoArcos(getArcos());
    }

    /**
     * Esta función realiza un recorrido iterativo y recursivo en búsqueda de arcos dirigidos
     * a diferencia del spaghetti, esta función termina su recorrido cuando encuentra un punto
     * donde hay varios segmentos que son incidentes (Es decir nodos, que son agregados al 
     * listado global de nodos), en esta caso realiza un procedimiento
     * recursivo para buscar otros arcos, pero termina el actual agregando el arco al listado
     * global de arcos dirigidos y le asigna sus respectivos nodos inicial y final.
     * 
     * @param arcoInicial  Segmento de recta tomado como referencia
     * @param anteriores Listado de segmentos que van conformando un arco dirigido
     * @param puntoInicio Punto Punto de referencia donde se busca el siguiente segmento
     */
    public void realizarRecorrido(SegmentoLinea arcoInicial,List<SegmentoLinea> anteriores,Punto puntoInicio)
    {
        Set<SegmentoLinea> incidentes = SegmentoLinea.segmentosPuntoFinal(segmentosCerrados,arcoInicial,puntoInicio);
        if(incidentes.size()>1)
        {
            Iterator<SegmentoLinea> recorrido = incidentes.iterator();
            while(recorrido.hasNext())
            {
                SegmentoLinea nuevo = recorrido.next();
                if(!SegmentoLinea.existeEnListaSegmentos(nuevo,segmentosIniciales))
                {
                    segmentosIniciales.add(nuevo);
                    List<SegmentoLinea> recorridoArcos = new ArrayList<SegmentoLinea>();
                    recorridoArcos.add(nuevo);
                    Punto siguiente = nuevo.getFin();
                    if(siguiente.esIgual(puntoInicio))
                        siguiente = nuevo.getOrigen();
                    realizarRecorrido(nuevo,recorridoArcos,siguiente);
                }
            }
            PoliLinea nPoli = PoliLinea.toPoliLinea(anteriores);
            Nodo nInicial = new Nodo();
            nInicial.setPunto(new Punto(nPoli.getPoliLinea().getCoordinateN(0)));
            String nEtiqueta=Nodo.existeEnListadoNodos(nodos, nInicial);
            if(nEtiqueta==null)
            {
                int  et = nodos.size();
                et++;
                String sEt= String.valueOf(et);
                nInicial.setEtiqueta(sEt);
                nodos.add(nInicial);
            }
            else nInicial.setEtiqueta(nEtiqueta);

            Nodo nFinal = new Nodo();
            nFinal.setPunto(new Punto(nPoli.getPoliLinea().getCoordinateN(nPoli.getPoliLinea().getNumPoints()-1)));
            nEtiqueta=Nodo.existeEnListadoNodos(nodos, nFinal);
            if(nEtiqueta==null)
            {
                int  et = nodos.size();
                et++;
                String sEt= String.valueOf(et);
                nFinal.setEtiqueta(sEt);
                nodos.add(nFinal);
            }
            else nFinal.setEtiqueta(nEtiqueta);

            ArcoDirigido aNuevo = new ArcoDirigido();
            aNuevo.setPoliLinea(nPoli.getPoliLinea());
            aNuevo.setInicial(nInicial);
            aNuevo.setFinal(nFinal);
            if(!ArcoDirigido.existeEnListadoArcos(arcos, aNuevo))
            {
                char et = Alfabeto.MINUSCULAS[getArcos().size()];
                String sEt= String.valueOf(et);
                aNuevo.setEtiqueta(sEt);
                getArcos().add(aNuevo);
            }
            conjuntoGenerado = nPoli.toConjuntoPuntos();
            conjuntoInicial = Punto.removerPuntos(conjuntoInicial, conjuntoGenerado);
        }
        else
        {
            SegmentoLinea siguiente = arcoInicial.seleccionarSegmentoSiguiente(incidentes,anteriores);
            if(siguiente!=null)
            {
                if(!SegmentoLinea.existeEnListaSegmentos(siguiente,anteriores))
                {
                    Punto envio = siguiente.getFin();
                    if(envio.esIgual(puntoInicio))
                        envio = siguiente.getOrigen();
                    anteriores.add(siguiente);
                    realizarRecorrido(siguiente,anteriores,envio);
                }
            }
        }
    }

    /**
     * @return the arcos
     */
    public List<ArcoDirigido> getArcos() {
        return arcos;
    }

    /**
     * @param arcos the arcos to set
     */
    public void setArcos(List<ArcoDirigido> arcos) {
        this.arcos = arcos;
    }

    public List<PoliLinea> getDouglasPeucker(double pTolerancia)
    {
        double tolerancia = pTolerancia*100;
        List<PoliLinea> polilineas = new ArrayList<PoliLinea>();
        for(int i = 0;i<arcos.size();i++)
        {
            GeometryFactory geom = new GeometryFactory();
            LineString lNueva = geom.createLineString(DouglasPeuckerLineSimplifier.simplify(arcos.get(i).getPoliLinea().getCoordinates(),tolerancia));
            PoliLinea nPoli = new PoliLinea();
            nPoli.setPoliLinea(lNueva);
            polilineas.add(nPoli);
        }
        return polilineas;
    }
}
